#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<ctype.h>
#include<fcntl.h>
#define LEFT "["
#define RIGHT "]"
#define LABLE "#"
#define DELIM " \t"
#define LINE_SIZE 1024
#define ARGC_SIZE 32
#define EXIT_CODE 44

//输入输出重定向
#define NONE -1
#define IN_RDIR 0 
#define OUT_RDIR 1
#define APPEND_RDIR 2
//声明环境变量
extern char**environ;
int lastcode=0;//本次进程的退出码

char pwd[LINE_SIZE];
//自定义环境变量表
char*myenv[LINE_SIZE];
char*rdirfilename=NULL;
int rdir=NONE;
const char*getusername()
{
    return  getenv("USER");
}

const char*gethostname()
{
    return getenv("HOSTNAME");
}

void getpwd()
{
    //获取当前路径的系统接口,直接将当前路径拷贝到pwd中
    getcwd(pwd,sizeof(pwd));
}

void check_redir(char*cmd)
{
    //ls -al -n
    //ls -al -n >/</>> filename.txt
    char*pos=cmd;
    while(*pos)
    {
        if(*pos=='>')
        {
            if(*(pos+1)=='>')
            {
                *pos++='\0';
                *pos++='\0';
                while(isspace(*pos)) pos++;
                rdirfilename=pos;
                rdir=APPEND_RDIR;
                break;
            }
            else
            {
                *pos++='\0';
                while(isspace(*pos)) pos++;
                rdir=OUT_RDIR;
                rdirfilename=pos;
                break;
            }
        }
        else if(*pos=='<')
        {
            *pos++='\0';
            //跳过空格符
            while(isspace(*pos)) pos++;
            rdirfilename=pos;
            //输入重定向
            rdir=IN_RDIR;
            break;
        }
        else{
            //do nothing
        }
        pos++;
    }
}

void Interact(char*cline,int size)
{
    getpwd();
    printf(LEFT"%s@%s %s"RIGHT""LABLE" ",getusername(),gethostname(),pwd);
    //在输入流中提取size字节到cline中
    char *s=fgets(cline,size,stdin);
    assert(s);
    (void)s;//避免s因未使用而导致编译出错
    cline[strlen(cline)-1]='\0';

    //ls -a -l > myfile.txt 检查是否有'>'和'<'符号
    check_redir(cline);
    
}

int splitstring(char*commandline,char*argv[])
{
    int i=0;
    argv[i++]=strtok(commandline,DELIM);
    //strtok函数会对任意一个出现在DELIM中的字符进行分割
    //对读到的内容进行分割 =为故意的设计
    while(argv[i++]=strtok(NULL,DELIM));
    //返回命令行参数的个数
    return i-1;
}

void normalExcute(char*_argv[])
{
     pid_t id=fork();
     if(id<0)
     {
        perror("fork error");
        return;
     }
     else if(id==0){
        int fd=0;
        if(rdir==IN_RDIR)
         {
            //输入重定向
            fd=open(rdirfilename,O_RDONLY);
            dup2(fd,0);
        }
        else if(rdir==OUT_RDIR)
        {
            //输出重定向  文件不存在时应设置权限
            fd=open(rdirfilename,O_WRONLY|O_CREAT|O_TRUNC,0666);
            dup2(fd,1);
        }
        else if(rdir==APPEND_RDIR)
        { 
            fd=open(rdirfilename,O_WRONLY|O_CREAT|O_APPEND,0666);
            dup2(fd,1);
         }
        
        //子进程执行命令
        //execvpe(_argv[0],_argv,environ);
        execvp(_argv[0],_argv);
        exit(EXIT_CODE);
     }
     else{
        int status=0;
        pid_t rid=waitpid(id,&status,0);
        if(rid==id){
            //获取退出码
            lastcode=WEXITSTATUS(status);    
        }
     }
}

int main()
{
    //声明环境变量
    //extern char**environ;
    int i=0;
    char commandline[LINE_SIZE];
    char*argv[ARGC_SIZE];
    int quit=0;
    while(!quit)
    {
        //重新初始化重定向内容
        rdirfilename=NULL;
        rdir=NONE;

        //交互命令，获取命令行
        Interact(commandline,sizeof(commandline));
        //printf("echo : %s\n",commandline);
        int argc=splitstring(commandline,argv);
        if(argc==0) continue;
        //演示是否正确读入命令行参数
        //for(int i=0;argv[i];i++) 
        //    printf("[%d]: %s\n",i,argv[i]);
        
        //判断是否为内建命令(直接穷举)
        //通过以下代码我们也可以得知，内建命令是由shell自己执行的
        //并不创造子进程来完成
        if(argc==2&&strcmp(argv[0],"cd")==0){
            //更改当前文件的路径
            chdir(argv[1]);
            //更新当前路径
            getpwd();

            sprintf(getenv("PWD"),"%s",pwd);
            continue; 
        }
        //ls为特殊处理
        else if(strcmp(argv[0],"ls")==0)
        {
            //输出的时候带上颜色
            argv[argc++]="--color";
            argv[argc]=NULL;
        }
        else if(argc==2&&strcmp(argv[0],"export")==0)
        {
            //putenv只是将将argv[1]的地址储存在环境变量中,一旦argv[1]中
            //内容发生改变，就找不到之前存入的环境变量了
            //putenv(argv[1]); 因此需要改写
            static int envsize=0;
            myenv[envsize]=(char*)malloc(sizeof(argv[1])+1);
            strcpy(myenv[envsize],argv[1]);
            putenv(myenv[envsize]);
            envsize++;
            continue;
        }
        else if(argc==2&&strcmp(argv[0],"echo")==0)
        {
            //echo $? 为输出上一次进程的退出码
            if(strcmp(argv[1],"$?")==0)
            {
                printf("%d\n",lastcode);
                lastcode=0;
            }
            //$为输出环境变量
           else if(*argv[1]=='$'){
                char*val=getenv(argv[1]+1);
                //判断环境变量是否获取成功
                if(val) printf("%s\n",val);
            }
            else{
                int num=strlen(argv[1]);
                char str[num];
                int j=0;
                for(int i=1;i<num-1;i++) str[j++]=argv[1][i];
                str[j]='\0';
                printf("%s\n",str);
            }
            continue;
        }
        //普通指令的执行
        normalExcute(argv);
    }
    return 0;
}

